﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace FasterWPF
{
    public static class Factory
    {
        public static FrameworkElementFactory SetProperty(this FrameworkElementFactory fef, DependencyProperty depProperty, object value)
        {
            fef.SetValue(depProperty, value);
            return fef;
        }

        #region StackPanel

        public static StackPanel InitStackPanel(double width, double height, ContainerType containerType, Orientation orientation, Brush background)
        {
            StackPanel stkcpanel2 = new StackPanel();
            stkcpanel2.Initialize(width, height, containerType, orientation);
            stkcpanel2.Background = background;

            return stkcpanel2;
        }

        public static StackPanel InitStackPanel(ContainerType containerType, Orientation orientation, Brush background, int? rowSpan, int? colSpan)
        {
            StackPanel stkcpanel2 = new StackPanel();
            stkcpanel2.Initialize(containerType, orientation, rowSpan, colSpan);
            stkcpanel2.Background = background;

            return stkcpanel2;
        }
        #endregion
        
        #region Menu and MenuItem

        public static T EndInit<T>(this SettingsChain<T> chain)
            where T: FrameworkElement
        {
            return chain.Item1;
        }

        public static SettingsChain<Menu> BeginMenuSettings(double width, double height, ContainerType containerType, Brush background)
        {
            return FasterWPF.Factory.InitMenu(width, height, containerType, background).BeginSettings<Menu>();
        }

        public static SettingsChain<Menu> BeginMenuSettings(double width, double height, ContainerType containerType)
        {
            return FasterWPF.Factory.InitMenu(width, height, containerType).BeginSettings<Menu>();
        }

        public static MenuItem InitMenuItem(string header, Menu mnu, Brush foreground, FontSettings fs, Brush outerBorderColor)
        {
            MenuItem mnuItem = new MenuItem();

            mnuItem.Initialize(double.NaN, double.NaN, ContainerType.HorizontalPanel, foreground, fs, outerBorderColor);
            mnuItem.Header = header;
            return mnuItem;
        }
        
        public static Menu InitMenu(double width, double height, ContainerType containerType, Brush background)
        {
            Menu mnu = new Menu();
            return MenuExt.Initialize(mnu, width, height, containerType, background);
        }

        public static Menu InitMenu(double width, double height, ContainerType containerType)
        {
            Menu mnu = new Menu();
            return MenuExt.Initialize(mnu, width, height, containerType);
        }

        public static MenuItem InitMenuItem(string header, Menu mnu)
        {
            MenuItem mnuItem = new MenuItem();
           
            mnuItem.Initialize(double.NaN, double.NaN, ContainerType.HorizontalPanel);
            mnuItem.Header = header;
            return mnuItem;
        }

        public static InnerChain<Menu> AddMenuItem(this InnerChain<Menu> innerMnu, Action<object, RoutedEventArgs> clickAction)
        {
            return innerMnu.AddMenuItem(double.NaN, double.NaN, clickAction);
        }

        public static InnerChain<Menu> AddImageToMenuItem(this InnerChain<Menu> innerMnu, string path, UriKind uriKind)
        {
            MenuItem mnuItem = (innerMnu.Data1 as MenuItem);
            Image finalImage = new Image();
            finalImage.Width = 24D;

            var uriSource = new Uri(path, uriKind);  //@"pack://siteoforigin:,,,/Images/4.png", UriKind.Absolute);
            var myImage = new BitmapImage(uriSource);
            myImage.Freeze();
            finalImage.Source = myImage;
            mnuItem.Icon = finalImage;

            return innerMnu;
        }

        public static InnerChain<Menu> AddMenuItem(this InnerChain<Menu> innerMnu, double mouseOverRectangleWidth, double mouseOverRectangleHeight, Action<object, RoutedEventArgs> clickAction)
        {
            MenuItem mnuItem = (innerMnu.Data1 as MenuItem);
            InnerChain<Menu> mnuChain = innerMnu.AddExisting<MenuItem, Menu>(0, 0, mnuItem)
                .SubscribeEventOnChild<MenuItem, Menu, RoutedEventArgs>(0, 0, "Click", clickAction);

            mnuItem.ChangeBackgroundColorOnMenuItem((mnuChain.Data3 as Brush));
            mnuItem.SetMenuItemGlyphPanelMarginToZero();
            if (mnuChain.Data2 != null && mnuChain.Data8 != null)
            {
                mnuItem.SetMouseOverColorOnMenuItem(mouseOverRectangleWidth, mouseOverRectangleHeight, (mnuChain.Data2 as Brush), (mnuChain.Data5 as Brush), (mnuChain.Data8 as Brush));
                mnuItem.SetMouseOverForegroundOnMenuItem((mnuChain.Data4 as Brush));
            }

            if (mnuChain.Data6 != null)
            {
                mnuItem.ClipToBounds = false;
                mnuItem.Width = (double)mnuChain.Data6;
                mnuItem.Height = (double)mnuChain.Data7;
            }
            
            mnuChain.Data1 = mnuItem;

            return mnuChain;
        }

        public static Menu EndMenuSettings(this SettingsChain<Menu> mnuChain)
        {
            return mnuChain.EndSettings<Menu>().EndInit();
        }

        public static InnerChain<Menu> SetMouseOverBorderColor(this InnerChain<Menu> mnuChain, Brush innerBorderColor, Brush outerborderColor)
        {
            mnuChain.Data5 = innerBorderColor;
            mnuChain.Data8 = outerborderColor;
            return mnuChain;
        }

        public static InnerChain<Menu> BeginMenuComposite(string header, Menu mnu, string mnuGuid, Brush mouseOverColor, Brush background, Brush mouseOverForeground, Brush foreground, string family, double fontSize, FontWeight weights, FontStyle fontStyle, Brush outerBorderColor)
        {
            MenuItem mnuItem = FasterWPF.Factory.InitMenuItem(header, mnu, foreground, FontExt.CreateFont(family, fontSize, weights, fontStyle), outerBorderColor);
            
            InnerChain<Menu> mnuChain = mnu.BeginComposite<Menu>(mnuGuid);
            mnuChain.Data1 = mnuItem;
            mnuChain.Data2 = mouseOverColor;
            mnuChain.Data3 = background;
            mnuChain.Data4 = mouseOverForeground;
            return mnuChain;
        }

        public static InnerChain<Menu> BeginMenuComposite(string header, Menu mnu, Brush mouseOverColor, Brush background, Brush mouseOverForeground, Brush foreground, string family, double fontSize, FontWeight weights, FontStyle fontStyle, Brush outerBorderColor)
        {
            string mnuGuid = Guid.NewGuid().ToString();
            MenuItem mnuItem = FasterWPF.Factory.InitMenuItem(header, mnu, foreground, FontExt.CreateFont(family, fontSize, weights, fontStyle), outerBorderColor);
            
            InnerChain<Menu> mnuChain = mnu.BeginComposite<Menu>(mnuGuid);
            mnuChain.Data1 = mnuItem;
            mnuChain.Data2 = mouseOverColor;
            mnuChain.Data3 = background;
            mnuChain.Data4 = mouseOverForeground;

            return mnuChain;
        }

        public static InnerChain<Menu> BeginMenuComposite(double? width, double? height, string header, Menu mnu, Brush mouseOverColor, Brush background, Brush mouseOverForeground, Brush foreground, string family, double fontSize, FontWeight weights, FontStyle fontStyle, Brush outerBorderColor)
        {
            string mnuGuid = Guid.NewGuid().ToString();
            MenuItem mnuItem = FasterWPF.Factory.InitMenuItem(header, mnu, foreground, FontExt.CreateFont(family, fontSize, weights, fontStyle), outerBorderColor);
          
            InnerChain<Menu> mnuChain = mnu.BeginComposite<Menu>(mnuGuid);
            mnuChain.Data1 = mnuItem;
            mnuChain.Data2 = mouseOverColor;
            mnuChain.Data3 = background;
            mnuChain.Data4 = mouseOverForeground;
            //Data5 is border color
            mnuChain.Data6 = width;
            mnuChain.Data7 = height;
           // mnuChain.Data8 = outerBorderColor;
            return mnuChain;
        }

        public static InnerChain<Menu> BeginMenuComposite(string header, Menu mnu, string mnuGuid)
        {  
            MenuItem mnuItem = FasterWPF.Factory.InitMenuItem(header, mnu);
            InnerChain<Menu> mnuChain = mnu.BeginComposite<Menu>(mnuGuid);
            mnuChain.Data1 = mnuItem;
            return mnuChain;
        }
        
        public static InnerChain<Menu> BeginMenuComposite(string header, Menu mnu)
        {
            string mnuGuid = Guid.NewGuid().ToString();
            MenuItem mnuItem = FasterWPF.Factory.InitMenuItem(header, mnu);
            InnerChain<Menu> mnuChain = mnu.BeginComposite<Menu>(mnuGuid);
            mnuChain.Data1 = mnuItem;
            return mnuChain;
        }

        public static MenuItem EndMenuComposite(this InnerChain<Menu> innerMnu)
        {
            innerMnu.EndComposite<Menu, MenuArgs>(null);
            return (innerMnu.Data1 as MenuItem);
        }
        #endregion

        #region Tab and GroupBox Headers

        /// <summary>
        /// Create a ContentControl applying the ContemporaryStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static ContentControl NewTabHeader(TabControl tbCtl, Brush foreground, Brush tabLabelBackground, Brush colorOfTheX, Brush tabItemUnderlineColor, string tabText, bool includeColoredLine, Action<object, MouseButtonEventArgs> closeAction)
        {
            StackPanel stkPnl = new StackPanel();
            stkPnl.Orientation = Orientation.Horizontal;
            stkPnl.Children.Add(new TextBlock(new Run(tabText) { FontFamily = new System.Windows.Media.FontFamily("Tahoma"), FontSize = 13D }) { Foreground = foreground, Margin = new Thickness(2, 2, 3, 2) });
            TextBlock txBlock = new TextBlock(new Run("X") { FontFamily = new System.Windows.Media.FontFamily("Tahoma"), FontWeight = FontWeights.ExtraBold, FontSize = 14D }) { Foreground = colorOfTheX, Margin = new Thickness(1, 2, 3, 2), ToolTip = "Close", };
            stkPnl.Children.Add(txBlock);
            txBlock.PreviewMouseDown += (o, e) => { closeAction(tbCtl, e); };

            ContentControl cc = new ContentControl()
            {
                Content = (
                   new Border()
                   {
                       Background = tabLabelBackground,
                       BorderThickness = new Thickness(0, (includeColoredLine ? 2 : 0), 0, ((!includeColoredLine) ? 2 : 0)),
                       BorderBrush = tabItemUnderlineColor,
                       Child = stkPnl
                   })
            };

            return cc;
        }

        public static void CreateGroupBoxHeader(this GroupBox grpbx, string headerTitle, Brush headerBackground, Brush foreground, string titleFont, double titleFontSize, FontWeight titleFontWeight, FontStyle titleFontStyle, Brush borderBrush, Thickness borderThickness)
        {
            Label titleLabel = new Label();
            titleLabel.Content = headerTitle;
            titleLabel.Background = headerBackground;
            titleLabel.Foreground = foreground;
            FontSettings fontSettings = FontExt.CreateFont(titleFont, titleFontSize, titleFontWeight, titleFontStyle);
            FontExt.ApplyFontSettings(fontSettings, titleLabel);
            grpbx.Header = titleLabel;
            grpbx.BorderBrush = borderBrush;
            grpbx.BorderThickness = borderThickness;
        }

        #endregion

        #region GroupItem

        /// <summary>
        /// Create a Label via FrameworkElementFactory for use with ListBox or ListView Group Description
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static FrameworkElementFactory CreateLabelForGroupItem(double height, double width, double fontSize, FontWeight fontWeight, Thickness margin, Brush foreground, Brush background, Brush borderBrush, Thickness borderThickness, HorizontalAlignment horizontalAlignment)
        {
            FrameworkElementFactory fefr = new FrameworkElementFactory(typeof(Label))
                             .SetProperty(Label.FontWeightProperty, fontWeight)
                             .SetProperty(Label.MarginProperty, margin)
                             .SetProperty(Label.HeightProperty, height)
                             .SetProperty(Label.WidthProperty, width)
                             .SetProperty(Label.FontSizeProperty, fontSize)
                             .SetProperty(Label.ForegroundProperty, foreground)
                             .SetProperty(Label.BackgroundProperty, background)
                             .SetProperty(Label.BorderBrushProperty, borderBrush)
                             .SetProperty(Label.BorderThicknessProperty, borderThickness)
                             .SetProperty(Label.HorizontalAlignmentProperty, horizontalAlignment)
                             .SetProperty(Label.ContentProperty, new Binding() { Path = new PropertyPath("Tag"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Expander), 1) });

            return fefr;
        }

        /// <summary>
        /// Create Expander for use with ListBox or ListView Group Description. Set factoryLabel to null if passing into method SetGroupExpanderFrameworkElementFactory.
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static FrameworkElementFactory CreateExpanderForGroupItem(FrameworkElementFactory factoryLabel, bool isExpanded, Brush borderBrush, Thickness borderThickness)
        {
            FrameworkElementFactory factoryExpander = new FrameworkElementFactory(typeof(Expander))
                        .SetProperty(Expander.IsExpandedProperty, isExpanded)
                        .SetProperty(Expander.BorderBrushProperty, borderBrush)
                        .SetProperty(Expander.BorderThicknessProperty, borderThickness);

            if (factoryLabel != null)
            {
                DataTemplate templateForLabel = new DataTemplate(typeof(GroupItem));
                DataTemplate templateForItemsControl = new DataTemplate(typeof(ContentControl));

                MultiBinding mb25 = new MultiBinding();
                mb25.Bindings.Add(new Binding("Name"));
                mb25.Bindings.Add(new Binding("ItemCount"));
                GroupLabelConverter glcon25 = new GroupLabelConverter();
                mb25.Converter = glcon25;

                templateForLabel.VisualTree = factoryLabel;

                FrameworkElementFactory factoryPresenter = new FrameworkElementFactory(typeof(ItemsControl));
                factoryPresenter.SetValue(ItemsControl.ItemsSourceProperty, new Binding() { Path = new PropertyPath("Content"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Expander), 1) });
                templateForItemsControl.VisualTree = factoryPresenter;

                factoryExpander.SetValue(Expander.HeaderTemplateProperty, templateForLabel);
                factoryExpander.SetValue(Expander.ContentTemplateProperty, templateForItemsControl);
                factoryExpander.SetValue(Expander.TagProperty, mb25);
                factoryExpander.SetValue(Expander.ContentProperty, new Binding() { Path = new PropertyPath("Items") });
            }

            return factoryExpander;
        }

        /// <summary>
        /// Create Expander for use with ListBox or ListView Group Description. Set factoryLabel to null if passing into method SetGroupExpanderFrameworkElementFactory.
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static FrameworkElementFactory CreateExpanderForGroupItem(FrameworkElementFactory factoryLabel, bool isExpanded, Brush borderBrush, Thickness borderThickness, string circleColor, string arrowColor, string mouseOverCircleColor, string mouseOverArrowColor, string pressedCircleColor, string pressedArrowColor, string headerBackgroundColor, FasterWPF.ExpanderExt.Shape es)
        {
            FrameworkElementFactory factoryExpander = new FrameworkElementFactory(typeof(Expander))
                        .SetProperty(Expander.IsExpandedProperty, isExpanded)
                        .SetProperty(Expander.BorderBrushProperty, borderBrush)
                        .SetProperty(Expander.TemplateProperty, ExpanderExt.ApplyExpanderStrategyFull(circleColor, arrowColor, mouseOverCircleColor, mouseOverArrowColor, pressedCircleColor, pressedArrowColor, headerBackgroundColor, es))
                        .SetProperty(Expander.BorderThicknessProperty, borderThickness);

            if (factoryLabel != null)
            {
                DataTemplate templateForLabel = new DataTemplate(typeof(GroupItem));
                DataTemplate templateForItemsControl = new DataTemplate(typeof(ContentControl));

                MultiBinding mb25 = new MultiBinding();
                mb25.Bindings.Add(new Binding("Name"));
                mb25.Bindings.Add(new Binding("ItemCount"));
                GroupLabelConverter glcon25 = new GroupLabelConverter();
                mb25.Converter = glcon25;

                templateForLabel.VisualTree = factoryLabel;

                FrameworkElementFactory factoryPresenter = new FrameworkElementFactory(typeof(ItemsControl));
                factoryPresenter.SetValue(ItemsControl.ItemsSourceProperty, new Binding() { Path = new PropertyPath("Content"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Expander), 1) });
                templateForItemsControl.VisualTree = factoryPresenter;

                factoryExpander.SetValue(Expander.HeaderTemplateProperty, templateForLabel);
                factoryExpander.SetValue(Expander.ContentTemplateProperty, templateForItemsControl);
                factoryExpander.SetValue(Expander.TagProperty, mb25);
                factoryExpander.SetValue(Expander.ContentProperty, new Binding() { Path = new PropertyPath("Items") });
            }

            return factoryExpander;
        }

        /// <summary>
        /// Create Expander for use with ListBox or ListView Group Description. Set factoryLabel to null if passing into method SetGroupExpanderFrameworkElementFactory.
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static FrameworkElementFactory CreateExpanderForGroupItem(FrameworkElementFactory factoryLabel, bool isExpanded, Brush borderBrush, Thickness borderThickness, string circleColor, string arrowColor, string mouseOverCircleColor, string mouseOverArrowColor, string pressedCircleColor, string pressedArrowColor, string headerBackgroundColor, string expandedArrowGeometryPath, string collapsedArrowGeometryPath, FasterWPF.ExpanderExt.Shape es)
        {
            FrameworkElementFactory factoryExpander = new FrameworkElementFactory(typeof(Expander))
                        .SetProperty(Expander.IsExpandedProperty, isExpanded)
                        .SetProperty(Expander.BorderBrushProperty, borderBrush)
                        .SetProperty(Expander.TemplateProperty, ExpanderExt.ApplyExpanderStrategyFull(circleColor, arrowColor, mouseOverCircleColor, mouseOverArrowColor, pressedCircleColor, pressedArrowColor, headerBackgroundColor, expandedArrowGeometryPath, collapsedArrowGeometryPath, es))
                        .SetProperty(Expander.BorderThicknessProperty, borderThickness);

            if (factoryLabel != null)
            {
                DataTemplate templateForLabel = new DataTemplate(typeof(GroupItem));
                DataTemplate templateForItemsControl = new DataTemplate(typeof(ContentControl));

                MultiBinding mb25 = new MultiBinding();
                mb25.Bindings.Add(new Binding("Name"));
                mb25.Bindings.Add(new Binding("ItemCount"));
                GroupLabelConverter glcon25 = new GroupLabelConverter();
                mb25.Converter = glcon25;

                templateForLabel.VisualTree = factoryLabel;

                FrameworkElementFactory factoryPresenter = new FrameworkElementFactory(typeof(ItemsControl));
                factoryPresenter.SetValue(ItemsControl.ItemsSourceProperty, new Binding() { Path = new PropertyPath("Content"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Expander), 1) });
                templateForItemsControl.VisualTree = factoryPresenter;

                factoryExpander.SetValue(Expander.HeaderTemplateProperty, templateForLabel);
                factoryExpander.SetValue(Expander.ContentTemplateProperty, templateForItemsControl);
                factoryExpander.SetValue(Expander.TagProperty, mb25);
                factoryExpander.SetValue(Expander.ContentProperty, new Binding() { Path = new PropertyPath("Items") });
            }

            return factoryExpander;
        }

        /// <summary>
        /// Create GroupItem Style for use with ListBox or ListView Group Description
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Style CreateStyleForGroupItem(FrameworkElementFactory factoryExpander, Thickness margin)
        {
            ControlTemplate templateForExpander = new ControlTemplate(typeof(GroupItem));

            Style myStyle = new Style(typeof(GroupItem));
            Setter setter = new Setter();
            setter.Property = GroupItem.MarginProperty;
            setter.Value = margin;
            myStyle.Setters.Add(setter);

            Setter setterTemplate = new Setter();
            setterTemplate.Property = GroupItem.TemplateProperty;
            setterTemplate.Value = templateForExpander;
            myStyle.Setters.Add(setterTemplate);

            templateForExpander.VisualTree = factoryExpander;

            return myStyle;
        }
        #endregion

    }//end of class
}//end of namespace
